package com.jswl.training.springjpa.frame;

import com.jswl.training.springjpa.util.ByteUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

/**
 * 导航7B协议帧解析器类
 * <p>
 * 将UDP数据报（DatagramPacket）转换为包装好的数据帧（SocketPackage<Nav7BFrame>）
 */
public class Nav7BFramePacketDecoder {
    //region Fields

    /**
     * 记录当前正在处理的数据帧。当组帧完成时需要对其进行深拷贝并输出。
     */
    private Nav7BFrame currentFrame;

    /**
     * 0x7B连续出现的次数计数
     */
    private int uniqueFlagInContentCount;

    /**
     * 是否需要对信息类型的正确性进行判断
     */
    private final boolean checkInfoType;

    /**
     * 是否需要对数据位长度的有效性进行判断
     */
    private final boolean checkBitLength;

    /**
     * 是否需要对校验和进行判断
     */
    private final boolean checkCheckSum;

    /**
     * 定义缓存的可用大小
     * <p>
     * 默认值：-1
     * 正整数表示有意义，当缓存数据长度过长时需要对其进行清理；
     * 负数时表示不对缓存大小进行判断和自动清理。
     */
    private final int maxBufferSize;

    /**
     * 定义数据帧最大长度
     * <p>
     * 用于定义一个数据帧被允许的总长度。这个长度指有效长度，而非实际长度。数据帧长度过长时，将放弃对该帧的解析。
     */
    private final int maxFrameSize;

    /**
     * 用于生成固定长度的空解析结果，减少GC次数
     */
    private static final byte[] EMPTY_DATA_CONTENT_PROCESS_RESULT = new byte[0];

    //endregion

    //region Constructors

    /**


    /**
     * 生成一个导航部标准7B数据帧解码器
     * <p>
     * 解码器用于将使用Netty进行网络数据交互的通道接收数据进行解码，生成已定义的数据帧类实例。
     * 必须在该解码器之后加入一个ChannelHandler并对_7BFrame类型的实例进行处理来完成数据帧的处理。
     * <p>
     * 解析参数采用默认值：
     * 数据帧最大长度-5000
     * 接收缓存最大长度-无限制
     * 是否检查信息类型是否有效-false
     * 是否检查校验和是否相等-true
     * 是否检查数据位长度是否一致-false
     * 是否记录数据帧原始内容-false
     */
    public Nav7BFramePacketDecoder() {
        currentFrame = new Nav7BFrame();
        uniqueFlagInContentCount = 0;

        this.maxFrameSize = 5000;
        this.maxBufferSize = -1;

        checkInfoType = false;
        checkCheckSum = true;
        checkBitLength = false;
    }

    //endregion


    //region Private Methods

    /**
     * 尝试对缓存中的数据进行解帧
     *
     * @param buff 缓存
     * @return 解析出的数据帧
     * @throws Exception 解析时出现的异常
     */
    private Nav7BFrame decode(ByteBuf buff) throws Exception {
        int readableBytes = buff.readableBytes();
        if (readableBytes < 16
                || (maxBufferSize > 0 && readableBytes > maxBufferSize)) {
            buff.skipBytes(readableBytes);
            return null;
        } else {
            if (parseFrame(buff)) {
                return generateAssembledFrame();
            } else {
                return null;
            }
        }
    }

    /**
     * 处理数据帧的详细内容，尝试解析出数据帧实例
     *
     * @param buff 数据缓存
     * @return 本次解析是否成功，且允许继续解析。
     */
    private boolean parseFrame(ByteBuf buff) {
        byte[] bytes;

        //region 帧头
        for (int i = 0; i < 3; ++i) {
            if (buff.readByte() != InternalShared.UNIQUE_FLAG) {
                return false;
            }
        }
        //endregion
        //region 信源
        byte sourceDeviceID = buff.readByte();
        byte sourceBoardID = buff.readByte();
        //设备地址中不允许出现0x7B
        if (sourceDeviceID != InternalShared.UNIQUE_FLAG) {
            currentFrame.setSourcePrimaryId(sourceDeviceID);
            currentFrame.setSourceSecondaryId(sourceBoardID);
        } else {
            return false;
        }
        //endregion
        //region 信宿
        byte targetDeviceID = buff.readByte();
        byte targetBoardID = buff.readByte();
        //设备地址中不允许出现0x7B
        if (targetDeviceID != InternalShared.UNIQUE_FLAG) {
            currentFrame.setTargetPrimaryId(targetDeviceID);
            currentFrame.setTargetSecondaryId(targetBoardID);
        } else {
            return false;
        }
        //endregion
        //region 信息类型
        if (buff.readableBytes() > 0) {
            byte value = buff.readByte();
            if (value == InternalShared.UNIQUE_FLAG) {
                //如果信息类型是0x7B，认为其是无效帧
                return false;
            } else {
                if (!checkInfoType || InternalShared.isValidInfoType(value)) {
                    currentFrame.setInfoType(value);
                } else {
                    return false;
                }
            }
        } else {
            return false;
        }
//        bytes = processBufferPerByte(buff, 1);
//        if (bytes.length > 0) {
//            byte infoType = bytes[0];
//            if (!checkInfoType || InternalShared.isValidInfoType(infoType)) {
//                currentFrame.setInfoType(infoType);
//            } else {
//                return false;
//            }
//        } else {
//            return false;
//        }
        //endregion
        //region 信息长度
        bytes = processBufferPerByte(buff, 2);
        if (bytes.length == 2) {
            int length = ByteUtils.bytesToUnsignedShortLE(bytes, 0);
            if (length + 12 <= maxFrameSize) {
                currentFrame.setInfoLength(length);
            } else {
                return false;
            }
        } else {
            return false;
        }
        //endregion
        //region 数据类型
        bytes = processBufferPerByte(buff, 2);
        if (bytes.length == 2) {
            currentFrame.setDataType(ByteUtils.bytesToUnsignedShortLE(bytes, 0));
        } else {
            return false;
        }
        //endregion
        //region 数据位长度
        bytes = processBufferPerByte(buff, 2);
        if (bytes.length == 2) {
            int bitLength = ByteUtils.bytesToUnsignedShortLE(bytes, 0);
            if (!checkBitLength
                    || (bitLength >= 0 && bitLength <= ((currentFrame.getInfoLength() - 4) * 8))) {
                currentFrame.setBitLength(bitLength);
            } else {
                return false;
            }
        } else {
            return false;
        }
        //endregion
        //region 数据内容
        int length = currentFrame.getInfoLength() - 4;
        if (length > 0) {
            bytes = processBufferPerByte(buff, length);
            if (bytes.length > 0) {
                currentFrame.setDataContent(Unpooled.wrappedBuffer(bytes));
            } else {
                return false;
            }
        } else if (length == 0) {
            currentFrame.setDataContent(Unpooled.EMPTY_BUFFER);
        } else {
            return false;
        }
        //endregion
        //region 校验
        if (buff.readableBytes() > 0) {
            byte checksum = buff.readByte();
            if (checkCheckSum && InternalShared.computeCheckSum(currentFrame) != checksum) {
                return false;
            }
        } else {
            return false;
        }
        //endregion
        //region 帧尾
        if (buff.readableBytes() > 0) {
            return buff.readByte() == InternalShared.FRAME_FINISH_FLAG;
        } else {
            return false;
        }
        //endregion
    }

    /**
     * 从缓存中按字节取出数据
     *
     * @param buff  缓存实例
     * @param count 需要取出的字节数
     * @return 取出的字节数组。如果处理失败，则返回长度为0的空数组
     */
    private byte[] processBufferPerByte(ByteBuf buff, int count) {
        int readable = buff.readableBytes();
        if (readable < count) {
            return EMPTY_DATA_CONTENT_PROCESS_RESULT;
        } else {
            int index = 0;
            buff.markReaderIndex();
            byte[] processedBuffer = new byte[count];

            do {
                if (buff.readableBytes() > 0) {
                    byte value = buff.readByte();
                    switch (check7BInFrameBody(value)) {
                        case 1: {
                            processedBuffer[index++] = value;
                            break;
                        }
                        case 0: {
                            break;
                        }
                        default: {
                            buff.resetReaderIndex();
                            return EMPTY_DATA_CONTENT_PROCESS_RESULT;
                        }
                    }
                } else {
                    buff.resetReaderIndex();
                    return EMPTY_DATA_CONTENT_PROCESS_RESULT;
                }
            } while (index < count);

            return processedBuffer;
        }
    }

    /**
     * 检查帧结构中是否存在违规帧头
     *
     * @param value 输入的帧数据内容
     * @return 1:数据有效；0：无意义数据；-1：数据错误
     */
    private int check7BInFrameBody(byte value) {
        switch (uniqueFlagInContentCount) {
            case 0:
            case 1: {
                if (value == InternalShared.UNIQUE_FLAG) {
                    ++uniqueFlagInContentCount;
                } else {
                    uniqueFlagInContentCount = 0;
                }

                return 1;
            }
            case 2: {
                if (value == InternalShared.PREVENT_HEADER_FLAG) {
                    uniqueFlagInContentCount = 0;
                    return 0;
                } else {
                    uniqueFlagInContentCount = 0;
                    return -1;
                }
            }
            default: {
                return -1;
            }
        }
    }

    /**
     * 生成数据帧实例
     *
     * @return 生成的数据帧实例
     */
    private Nav7BFrame generateAssembledFrame() {
        Nav7BFrame newFrame = currentFrame;
        currentFrame = new Nav7BFrame();

        return newFrame;
    }

    //endregion
}
